package com.huanletao.okhttp.interceptors;

import okhttp3.*;
import okio.Buffer;
import okio.BufferedSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * HTTP 请求/响应日志拦截器（通用、可配置、安全）
 * - 支持按需打印：请求头、请求体、响应头、响应体
 * - 自动跳过文件上传/下载
 * - 敏感字段脱敏（token, password, auth 等）
 * - 安全读取响应体（peek，不消费原始流）
 * - 最大 body 打印长度可配置
 * - 日志输出方式由子类决定（可输出到指定文件）
 */
public abstract class LoggingInterceptor implements Interceptor {

    private final Logger log = LoggerFactory.getLogger(LoggingInterceptor.class);

    // 文件类 Content-Type 关键词（自动跳过打印）
    private static final String[] FILE_CONTENT_TYPES = {
            "multipart/form-data",
            "application/octet-stream",
            "image/",
            "video/",
            "audio/",
            "application/pdf",
            "application/zip",
            "application/x-rar-compressed",
            "application/msword",
            "application/vnd.ms-excel",
            "application/vnd.openxmlformats-officedocument"
    };

    // ===== 可配置项 =====
    protected int maxLoggableSize = 1024;
    protected boolean enabled = true;

    // ===== 枚举：日志详细级别 =====
    public enum LogDetailLevel {
        NONE,
        REQUEST_HEADERS,
        REQUEST_BODY,
        RESPONSE_HEADERS,
        RESPONSE_BODY,
        ALL // 等同于 RESPONSE_BODY
    }

    // ===== 配置方法（支持链式调用）=====

    public abstract LogDetailLevel getLogLevel();

    public LoggingInterceptor setMaxLoggableSize(int maxLoggableSize) {
        this.maxLoggableSize = maxLoggableSize > 0 ? maxLoggableSize : 1024;
        return this;
    }

    public LoggingInterceptor setEnabled(boolean enabled) {
        this.enabled = enabled;
        return this;
    }

    // ===== 判断方法 =====

    protected boolean isLogEnabled() {
        return enabled && getLogLevel() != LogDetailLevel.NONE;
    }

    protected boolean isLogRequestHeaders() {
        return isLogEnabled() && getLogLevel().compareTo(LogDetailLevel.REQUEST_HEADERS) >= 0;
    }

    protected boolean isLogRequestBody() {
        return isLogEnabled() && getLogLevel().compareTo(LogDetailLevel.REQUEST_BODY) >= 0;
    }

    protected boolean isLogResponseHeaders() {
        return isLogEnabled() && getLogLevel().compareTo(LogDetailLevel.RESPONSE_HEADERS) >= 0;
    }

    protected boolean isLogResponseBody() {
        return isLogEnabled() && getLogLevel().compareTo(LogDetailLevel.RESPONSE_BODY) >= 0;
    }

    // ===== 核心拦截逻辑 =====

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();

        if (!isLogEnabled()) {
            return chain.proceed(request);
        }

        String requestId = UUID.randomUUID().toString().substring(0, 8);
        long startTime = System.nanoTime();

        try {
            // --- 打印请求行 ---
            if (isLogRequestHeaders()) {
                safeLog(">>> [" + requestId + "] " + request.method() + " " + request.url());
            }

            // --- 打印请求头（脱敏）---
            if (isLogRequestHeaders()) {
                Headers headers = request.headers();
                for (int i = 0; i < headers.size(); i++) {
                    String name = headers.name(i);
                    String value = isSensitiveHeader(name) ? "***" : headers.value(i);
                    safeLog(">>> [" + requestId + "] header " + name + ": " + value);
                }
            }

            // --- 打印请求体 ---
            RequestBody requestBody = request.body();
            if (isLogRequestBody() && requestBody != null) {
                MediaType contentType = requestBody.contentType();
                if (isFileUpload(contentType, request.headers())) {
                    safeLog(">>> [" + requestId + "] requestBody: [file upload skipped]");
                } else {
                    String bodyStr = getRequestBodyString(requestBody);
                    if (bodyStr.length() > maxLoggableSize) {
                        safeLog(">>> [" + requestId + "] requestBody: [too large, len=" + bodyStr.length() + "]");
                    } else {
                        safeLog(">>> [" + requestId + "] requestBody: " + bodyStr);
                    }
                }
            }

            // === 执行请求 ===
            Response response = chain.proceed(request);
            long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);

            // --- 打印响应行 ---
            if (isLogResponseHeaders()) {
                safeLog("<<< [" + requestId + "] " + response.code() + " " + response.message() + " (" + duration + "ms)");
            }

            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                // --- 打印响应头 ---
                if (isLogResponseHeaders()) {
                    Headers headers = response.headers();
                    for (int i = 0; i < headers.size(); i++) {
                        String name = headers.name(i);
                        String value = headers.value(i);
                        safeLog("<<< [" + requestId + "] header " + name + ": " + value);
                    }
                }

                // --- 打印响应体 ---
                if (isLogResponseBody()) {
                    if (isFileDownload(responseBody.contentType(), response.headers())) {
                        long contentLength = responseBody.contentLength();
                        String sizeStr = contentLength >= 0 ? formatFileSize(contentLength) : "unknown";
                        safeLog("<<< [" + requestId + "] responseBody: [file download skipped, size=" + sizeStr + "]");
                    } else {
                        String bodyStr = getResponseBodyString(responseBody);
                        if (bodyStr.length() > maxLoggableSize) {
                            safeLog("<<< [" + requestId + "] responseBody: [too large, len=" + bodyStr.length() + "]");
                        } else {
                            safeLog("<<< [" + requestId + "] responseBody: " + bodyStr);
                        }
                    }
                }
            }

            if (isLogResponseBody()) {
                safeLog("<<< [" + requestId + "] --- END HTTP ---");
            }

            return response;

        } catch (Exception e) {
            safeLog(">>> [" + requestId + "] 请求失败: " + e.getClass().getSimpleName() + " - " + e.getMessage());
            throw e;
        }
    }

    // ==================== 工具方法 ====================

    /**
     * 判断是否为文件上传（如 multipart/form-data）
     */
    private boolean isFileUpload(MediaType contentType, Headers headers) {
        if (contentType == null) return false;
        String mimeType = contentType.toString().toLowerCase();
        for (String type : FILE_CONTENT_TYPES) {
            if (mimeType.contains(type.toLowerCase())) return true;
        }
        return false;
    }

    /**
     * 判断是否为文件下载（附件或常见文件类型）
     */
    private boolean isFileDownload(MediaType contentType, Headers headers) {
        if (contentType == null) return false;
        String mimeType = contentType.toString().toLowerCase();
        for (String type : FILE_CONTENT_TYPES) {
            if (mimeType.contains(type.toLowerCase())) return true;
        }
        String contentDisposition = headers.get("Content-Disposition");
        return contentDisposition != null && contentDisposition.contains("attachment");
    }

    /**
     * 安全读取请求体（不消费原始流）
     */
    private String getRequestBodyString(RequestBody requestBody) {
        try {
            Buffer buffer = new Buffer();
            requestBody.writeTo(buffer);

            Charset charset = StandardCharsets.UTF_8;
            MediaType contentType = requestBody.contentType();
            if (contentType != null) {
                charset = contentType.charset(StandardCharsets.UTF_8);
            }

            return buffer.readString(charset);
        } catch (Exception e) {
            return "[body read failed: " + e.getMessage() + "]";
        }
    }

    /**
     * 安全读取响应体（使用 peek，不消费原始流）
     */
    private String getResponseBodyString(ResponseBody responseBody) {
        try {
            BufferedSource source = responseBody.source();
            source.request(Integer.MAX_VALUE); // 请求读取全部
            Buffer buffer = source.buffer();

            if (buffer.size() == 0) {
                return "[body empty]";
            }

            Buffer clone = buffer.clone();
            Charset charset = StandardCharsets.UTF_8;
            MediaType contentType = responseBody.contentType();
            if (contentType != null) {
                charset = contentType.charset(StandardCharsets.UTF_8);
            }

            return clone.readString(charset);
        } catch (Exception e) {
            return "[body read failed: " + e.getMessage() + "]";
        }
    }

    /**
     * 判断是否为敏感头（如 token、password）
     */
    private boolean isSensitiveHeader(String name) {
        if (name == null) return false;
        String lowerName = name.toLowerCase();
        return lowerName.contains("token")
                || lowerName.contains("pass")
                || lowerName.contains("key")
                || lowerName.contains("auth")
                || lowerName.contains("secret")
                || lowerName.contains("cookie");
    }

    /**
     * 格式化文件大小
     */
    private String formatFileSize(long bytes) {
        if (bytes < 0) return "unknown";
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
    }

    /**
     * 安全调用日志输出，捕获所有异常，避免中断请求
     */
    private void safeLog(String msg) {
        try {
            if (msg != null && !msg.isEmpty()) {
                log(msg);
            }
        } catch (Throwable t) { // 捕获 Throwable，防止 Error 传播
            // 使用父类内置 logger 记录日志失败（避免递归）
            log.warn("LoggingInterceptor: 子类日志实现异常 - " + t.getMessage());
            // 可选：开发环境打印堆栈
            if (log.isDebugEnabled()) {
                log.debug("LoggingInterceptor: log method failed stack trace", t);
            }
        }
    }

    /**
     * 抽象日志输出方法（由子类实现）
     */
    public abstract void log(String msg);
}